package cz.data.domain.market.mapping.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import cz.data.common.base.BaseResponse;
import cz.data.common.base.SimpleDataResponse;
import cz.data.common.base.TableDataResponse;
import cz.data.common.support.db.DbType;
import cz.data.common.support.db.core.DbColumn;
import cz.data.common.support.db.core.DbTable;
import cz.data.common.support.db.driver.DataOperation;
import cz.data.common.support.db.manage.Condition;
import cz.data.domain.driver.rpc.ElasticsearchDriverFeign;
import cz.data.domain.driver.rpc.IDriverFeign;
import cz.data.domain.driver.rpc.JdbcDriverFeign;
import cz.data.domain.market.model.entity.TableApiEntity;
import cz.data.domain.metadata.model.MetadataUtil;
import cz.data.domain.metadata.model.entity.MetadataSourceEntity;
import cz.data.domain.metadata.rpc.MetadataFeign;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;

@Component
public class TableApiEngine {

    @Resource
    MetadataFeign metadataFeign;
    @Resource
    JdbcDriverFeign jdbcDriverFeign;
    @Resource
    ElasticsearchDriverFeign elasticsearchDriverFeign;

    public BaseResponse insertOperate(TableApiEntity tableApi, JSONObject record) {
        MetadataSourceEntity source = metadataFeign.getMetadataSourceById(tableApi.getSourceId());
        DbTable dbTable = MetadataUtil.createDbTable(tableApi.getTableId(),
                metadataFeign::getMetadataTableById,
                metadataFeign::innerMetadataColumnByTableId);
        DataOperation operation = new DataOperation();
        operation.setSource(tableApi.getSourceId());
        operation.setTable(dbTable);
        operation.setData(record);

        IDriverFeign driverFeign = getDriverFeign(source.getDbType());
        Boolean response = driverFeign.insertData(operation);
        return BaseResponse.withBoolean(Boolean.TRUE.equals(response));
    }

    public BaseResponse updateOperate(TableApiEntity tableApi, JSONObject record) {
        MetadataSourceEntity source = metadataFeign.getMetadataSourceById(tableApi.getSourceId());
        DbTable dbTable = MetadataUtil.createDbTable(tableApi.getTableId(),
                metadataFeign::getMetadataTableById,
                metadataFeign::innerMetadataColumnByTableId);
        DataOperation operation = new DataOperation();
        operation.setSource(tableApi.getSourceId());
        operation.setTable(dbTable);
        operation.setData(record);

        IDriverFeign driverFeign = getDriverFeign(source.getDbType());
        Boolean response = driverFeign.updateData(operation);
        return BaseResponse.withBoolean(Boolean.TRUE.equals(response));
    }

    public BaseResponse removeOperate(TableApiEntity tableApi, JSONObject primaryKeys) {
        MetadataSourceEntity source = metadataFeign.getMetadataSourceById(tableApi.getSourceId());
        DbTable dbTable = MetadataUtil.createDbTable(tableApi.getTableId(),
                metadataFeign::getMetadataTableById,
                metadataFeign::innerMetadataColumnByTableId);
        List<DbColumn> columns = dbTable.getColumns();
        JSONObject record = new JSONObject();
        for (DbColumn column : columns) {
            if (Boolean.TRUE.equals(column.getColKey())) {
                Object value = primaryKeys.get(column.getColName());
                record.put(column.getColName(), value);
            }
        }
        DataOperation operation = new DataOperation();
        operation.setSource(tableApi.getSourceId());
        operation.setTable(dbTable);
        operation.setData(record);

        IDriverFeign driverFeign = getDriverFeign(source.getDbType());
        Boolean response = driverFeign.removeData(operation);
        return BaseResponse.withBoolean(Boolean.TRUE.equals(response));
    }

    public SimpleDataResponse<JSONObject> getByIdOperate(TableApiEntity tableApi, JSONObject primaryKeys) {
        MetadataSourceEntity source = metadataFeign.getMetadataSourceById(tableApi.getSourceId());
        DbTable dbTable = MetadataUtil.createDbTable(tableApi.getTableId(),
                metadataFeign::getMetadataTableById,
                metadataFeign::innerMetadataColumnByTableId);
        List<DbColumn> columns = dbTable.getColumns();
        JSONObject record = new JSONObject();
        for (DbColumn column : columns) {
            if (Boolean.TRUE.equals(column.getColKey())) {
                Object value = primaryKeys.get(column.getColName());
                record.put(column.getColName(), value);
            }
        }
        DataOperation operation = new DataOperation();
        operation.setSource(tableApi.getSourceId());
        operation.setTable(dbTable);
        operation.setData(record);

        IDriverFeign driverFeign = getDriverFeign(source.getDbType());
        JSONObject response = driverFeign.getDataById(operation);
        return new SimpleDataResponse<>(response);
    }

    public TableDataResponse<JSONObject> pageOperate(TableApiEntity tableApi, JSONObject params) {
        MetadataSourceEntity source = metadataFeign.getMetadataSourceById(tableApi.getSourceId());
        DbTable dbTable = MetadataUtil.createDbTable(tableApi.getTableId(),
                metadataFeign::getMetadataTableById,
                metadataFeign::innerMetadataColumnByTableId);
        DataOperation operation = new DataOperation();
        operation.setSource(tableApi.getSourceId());
        operation.setTable(dbTable);
        operation.setData(new JSONObject());

        List<String> fields = new ArrayList<>();
        List<Condition> conditions = new ArrayList<>();
        List<DbColumn> columns = dbTable.getColumns();
        for (DbColumn column : columns) {
            fields.add(column.getColName());
            Object value = params.get(column.getColName());
            if (Objects.nonNull(value)) {
                Condition condition = new Condition();
                condition.setColumn(column.getColName());
                condition.setQueryType("eq");
                condition.setValue(value);
                conditions.add(condition);
            }
        }
        operation.setFields(fields);
        operation.setConditions(conditions);
        int pageNum = Optional.ofNullable(params.getInteger("pageNum")).orElse(1);
        int pageSize = Optional.ofNullable(params.getInteger("pageSize")).orElse(20);
        operation.setPageNum(pageNum);
        operation.setPageSize(pageSize);

        IDriverFeign driverFeign = getDriverFeign(source.getDbType());
        Page<JSONObject> page = driverFeign.getPageData(operation);
        return new TableDataResponse<>(page.getRecords(), page.getTotal());
    }

    private IDriverFeign getDriverFeign(String dbType) {
        if (DbType.ELASTICSEARCH.getDb().equals(dbType)) {
            return elasticsearchDriverFeign;
        } else if (DbType.MYSQL.getDb().equals(dbType) ||
                DbType.MARIADB.getDb().equals(dbType) ||
                DbType.ORACLE.getDb().equals(dbType) ||
                DbType.ORACLE_12C.getDb().equals(dbType) ||
                DbType.SQL_SERVER.getDb().equals(dbType) ||
                DbType.SQL_SERVER2008.getDb().equals(dbType) ||
                DbType.POSTGRE_SQL.getDb().equals(dbType)) {
            return jdbcDriverFeign;
        } else {
            throw new UnsupportedOperationException("数据源不适配该操作:" + dbType);
        }
    }
}
